package per.sunmes.lesrb.manager;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.assets.loaders.FileHandleResolver;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.utils.Drawable;
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.Disposable;

import per.sunmes.lesrb.util.GM;
import per.sunmes.lesrb.util.U;

/**
 * RM副本
*/
public class CustomRM implements Disposable {

	protected String resourceDir = RM.RES_DIR;
	protected boolean isGetFileCheckAbsolut = RM.isGetFileCheckAbsolut;
	protected boolean isGetFileCheckInternal = RM.isGetFileCheckInternal;
	protected boolean isGetFileCheckExternal = RM.isGetFileCheckExternal;
	protected boolean isGetFileCheckResDirExternal = RM.isGetFileCheckResDirExternal;

	private static final String TAG = "CustomRM";

	////////////////
	FileHandleResolver resolver;
	AssetManager asset;

	Map<String, Disposable> cachedDisposables = new HashMap<String, Disposable>();
	Set<Disposable> disposables = new HashSet<Disposable>();
	Map<String, TextureRegion> regionsCache = new HashMap<String, TextureRegion>();
	Map<String, Drawable> drawableCache = new HashMap<String, Drawable>();
	Array<String> loadedAtlasNames = new Array<String>();

	public void initFileResolverAndAssets() {
		resolver = new FileHandleResolver() {
			public FileHandle resolve(String file) {
				return getFile(file);
			}
		};
		asset = new AssetManager(resolver);
	}

	public AssetManager getAsset() {
		return asset;
	}

	public FileHandleResolver getFileResolver() {
		return resolver;
	}

	public void addDisposable(Disposable dp) {
		if (!disposables.contains(dp)) {
			disposables.add(dp);
		}
	}

	public void addDisposable(String name, Disposable dp) throws RuntimeException {
		if (cachedDisposables.containsKey(name)) {
			throw new RuntimeException("A Disposable cache already used name :" + name);
		} else {
			cachedDisposables.put(name, dp);
		}
	}

	/**
	 * if name was cached, old one will disposed.
	 * 
	 * @param name
	 * @param dp
	 */
	public void addDisposableReplace(String name, Disposable dp) {
		if (cachedDisposables.containsKey(name)) {
			Disposable odp = cachedDisposables.get(name);
			if (odp != null && odp != dp) {
				odp.dispose();
			}
		}
		cachedDisposables.put(name, dp);
	}

	public Disposable getCache(String name) {
		return cachedDisposables.get(name);
	}

	public Map<String, TextureRegion> getRegionCache() {
		return regionsCache;
	}

	public Array<String> getLoadedAtlasNames() {
		return loadedAtlasNames;
	}

	public Drawable getDrawable(String name) {
		Drawable db = drawableCache.get(name);
		if (db == null) {
			db = new TextureRegionDrawable(loadGetEverywhere(name));
			drawableCache.put(name, db);
		}
		return db;
	}

	public void loadAtlasAndCache(String atlasName) {
		if (asset.isLoaded(atlasName, TextureAtlas.class)) {
			if (!loadedAtlasNames.contains(atlasName, false)) {
				loadedAtlasNames.add(atlasName);
			}
		} else {
			loadedAtlasNames.add(atlasName);
			asset.load(atlasName, TextureAtlas.class);
		}
	}

	public TextureRegion loadGetEverywhere(String textureName) {
		TextureRegion region;
		if (regionsCache.containsKey(textureName)) {
			return new TextureRegion(regionsCache.get(textureName));
		}
		for (String atlas : loadedAtlasNames) {
			region = loadTextureRegionFromAtlas(textureName, atlas);
			if (region != null) {
				regionsCache.put(textureName, region);
				return new TextureRegion(region);
			}
		}
		region = new TextureRegion(loadGetTextureFromFile(textureName));
		regionsCache.put(textureName, region);
		return new TextureRegion(region);

	}

	public Texture loadGetTextureFromFile(String textureName) {
		if (asset.isLoaded(textureName, Texture.class)) {
			return asset.get(textureName, Texture.class);
		} else {
			FileHandle fh = getFile(textureName);
			if (!fh.exists()) {
				GM.logger.error(TAG, "No texture named [" + textureName + "] found!");
				return RM.getWhiteRectTexture();
			} else {
				asset.load(textureName, Texture.class);
				asset.finishLoadingAsset(textureName);
				return asset.get(textureName, Texture.class);
			}
		}
	}

	public TextureRegion loadTextureRegionFromAtlas(String textureName, String atlasName) {
		if (!asset.isLoaded(atlasName, TextureAtlas.class)) {
			asset.load(atlasName, TextureAtlas.class);
			asset.finishLoadingAsset(atlasName);
		}
		TextureAtlas ta = asset.get(atlasName, TextureAtlas.class);
		if (ta == null) {
			return null;
		}
		AtlasRegion ar = ta.findRegion(textureName);
		if (ar == null) {
			int p = textureName.lastIndexOf('.');
			ar = ta.findRegion(textureName.substring(0, p));
		}
		if (ar != null) {
			return ar;
		} else {
			return null;
		}
	}

	public FileHandle getFile(String filePath) {
		FileHandle fh = null;
		if (isGetFileCheckInternal) {
			fh = Gdx.files.internal(filePath);
			if (fh.exists()) {
				return fh;
			}
		}
		if (isGetFileCheckExternal) {
			fh = Gdx.files.external(filePath);
			if (fh.exists()) {
				return fh;
			}
		}
		if (isGetFileCheckResDirExternal) {
			fh = new FileHandle(RM.RES_DIR + "/" + filePath);
			if (fh.exists()) {
				return fh;
			}
		}
		if (isGetFileCheckAbsolut) {
			fh = Gdx.files.absolute(filePath);
			if (fh.exists()) {
				return fh;
			}
		}
		return fh;
	}

	@Override
	public void dispose() {
		regionsCache.clear();
		loadedAtlasNames.clear();

		for (Disposable dp : disposables) {
			dp.dispose();
		}
		disposables.clear();

		for (Entry<String, Disposable> e : cachedDisposables.entrySet()) {
			if (e.getValue() != null) {
				e.getValue().dispose();
			}
		}
		cachedDisposables.clear();

		if (asset != null) {
			asset.dispose();
			asset = null;
		}
	}

	////////////
	public TextureRegion region(String name, TextureFilter filter) {
		TextureRegion region;
		region = RM.getRegion(name);
		if (filter != null) {
			Texture t = region.getTexture();
			if (t.getMagFilter() != filter || t.getMinFilter() != filter) {
				t.setFilter(filter, filter);
			}
		}
		return region;
	}

	public TextureRegion region(String name) {
		return region(name, U.usedTextureFilter);
	}

	public TextureRegion region(String name, int x, int y, int width, int height) {
		TextureRegion region;
		region = new TextureRegion(region(name).getTexture(), x, y, width, height);
		return region;
	}

	public Image image(String name, TextureFilter filter) {
		return new Image(region(name, filter));
	}

	public Image image(String name) {
		return new Image(drawable(name));
	}

	public Drawable drawable(String name) {
		TextureRegionDrawable drawable = (TextureRegionDrawable) RM.getCachedDrawable(name);
		Texture tex = drawable.getRegion().getTexture();
		if (tex.getMagFilter() != U.usedTextureFilter || tex.getMinFilter() != U.usedTextureFilter) {
			tex.setFilter(U.usedTextureFilter, U.usedTextureFilter);
		}
		return drawable;
	}

	public String getResourceDir() {
		return resourceDir;
	}

	public void setResourceDir(String resourceDir) {
		this.resourceDir = resourceDir;
	}

	public boolean isGetFileCheckAbsolut() {
		return isGetFileCheckAbsolut;
	}

	public boolean isGetFileCheckExternal() {
		return isGetFileCheckExternal;
	}

	public boolean isGetFileCheckInternal() {
		return isGetFileCheckInternal;
	}

	public boolean isGetFileCheckResDirExternal() {
		return isGetFileCheckResDirExternal;
	}

	public void setGetFileCheckAbsolut(boolean isGetFileCheckAbsolut) {
		this.isGetFileCheckAbsolut = isGetFileCheckAbsolut;
	}

	public void setGetFileCheckExternal(boolean isGetFileCheckExternal) {
		this.isGetFileCheckExternal = isGetFileCheckExternal;
	}

	public void setGetFileCheckInternal(boolean isGetFileCheckInternal) {
		this.isGetFileCheckInternal = isGetFileCheckInternal;
	}

	public void setGetFileCheckResDirExternal(boolean isGetFileCheckResDirExternal) {
		this.isGetFileCheckResDirExternal = isGetFileCheckResDirExternal;
	}
}
